package org.agony2d.gpu.core {
	import flash.display.Stage;
	import flash.display.Stage3D;
	import flash.display3D.Context3D;
	import flash.display3D.Context3DBlendFactor;
	import flash.display3D.Context3DCompareMode;
	import flash.display3D.Context3DTriangleFace;
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.geom.Matrix;
	import flash.geom.Matrix3D;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.utils.getTimer;
	
	import org.agony2d.Agony;
	import org.agony2d.core.IProcess;
	import org.agony2d.core.ProcessManager;
	import org.agony2d.core.agony_internal;
	import org.agony2d.debug.Logger;
	import org.agony2d.gpu.supportClasses.programs.ProgramProxy;
	import org.agony2d.gpu.supportClasses.textures.TextureManager;
	import org.agony2d.gpu.supportClasses.textures.TextureProxy;
	import org.agony2d.input.ATouchEvent;
	import org.agony2d.input.Touch;
	import org.agony2d.input.TouchManager;
	import org.agony2d.notify.AEvent;
	import org.agony2d.notify.Notifier;
	
	use namespace agony_internal;
	
	/** [ AAManager ]
	 *  [★]
	 * 		vs Starling (benchmark) :
	 * 
	 * 			1.  render performance (+80%) ★
	 *  		2.  facile batch (can be reset at any time) ★
	 *  		3.  perfect interact (all kinds of touch / mouse event) ★
	 *  		4.  texture (none...)
	 *  		5.  effect (node...)
	 *  		6.  perfect animator (?? high performance × lots of control api) ★
	 *  		7.  auto fit function... (??) ★
	 */
final public class AAManager extends Notifier implements IProcess{
	
	public function AAManager() {
		super(null)
	}
	
	agony_internal static function set enableErrorChecking( b:Boolean ) : void {
		if (g_enableErrorChecking != b) {
			g_enableErrorChecking = b
			if (g_context3d) {
				g_context3d.enableErrorChecking = b
			}
		}
	}
	
	agony_internal function startup( bgColor:uint,
									 hasMaskForAspectRatio:Boolean,
									 antiAlias:int, 
									 profile:String, 
									 maxNodeDepth:int,
									 stage3dIndex:int ) : void {
		if (!ProcessManager.g_stage) {
			Logger.reportError("AgonyAA", "startup", "AgonyCore has not started up...!!");
		}
		else if (g_orthoMatrix3d) {
			Logger.reportError("AgonyAA", "startup", "AgonyAA has been started up...!!")
		}
		g_bgColor = bgColor
		g_antiAlias = antiAlias
		g_fullWidth = NodeAA.g_fullWidth = Agony.g_fullWidth
		g_fullHeight = NodeAA.g_fullHeight = Agony.g_fullHeight
		
		NodeAA.cachedMatrix = TextureProxy.cachedMatrix = new Matrix
		NodeAA.cachedRect = TextureProxy.cachedRect = new Rectangle
		NodeAA.cachedPoint = new Point
		NodeAA.g_matrixQueue = new MatrixProp
		NodeAA.g_matrixQueue.expands(maxNodeDepth)
		NodeAA.g_textureList = TextureManager.g_textureList = {}
		NodeAA.g_convergence = ProgramProxy.g_convergence = new VertexConvergence
		NodeAA.g_orthoMatrix3d = ProgramProxy.g_orthoMatrix3d = g_orthoMatrix3d = new Matrix3D
		transformOrthoMatrix(0, 0 , g_fullWidth, g_fullHeight)
		
		TouchManager.getInstance().addEventListener(ATouchEvent.NEW_TOUCH, ____onNewTouch)
		ProcessManager.addFrameProcess(this, ProcessManager.GPU)
		
		g_stage3d = g_stage.stage3Ds[stage3dIndex]
		g_stage3d.addEventListener(Event.CONTEXT3D_CREATE, ____onContextCreated)
		g_stage3d.addEventListener(ErrorEvent.ERROR,       ____onError)
		g_stage3d.requestContext3D("auto", profile)
			
		trace("\n================================== [ Agony2d - Accelerated App For Mobile ] ==================================")
		Logger.reportMessage("AgonyAA", "★[ startup ]..." +
							"bgColor [ " + bgColor + " ]..." +
							"黑边遮罩 [ " + hasMaskForAspectRatio + " ]..." +
							"antiAlias [ " + antiAlias + " ]...", 2)
	}
	
	agony_internal function createWorld( width:int, height:int, invalidWhenLeave:Boolean ) : WorldAA {
		var world:WorldAA
		
		g_worldList[g_worldLength++] = world = new WorldAA(width, height, invalidWhenLeave)
		return world
	}
	
	agony_internal function getWorldAt( index:int ) : WorldAA {
		return g_worldList[index]
	}
	
	agony_internal static function removeWorld( world:WorldAA ) : void {
		var index:int
		
		index = g_worldList.indexOf(world)
		g_worldList.splice(index, 1)
		--g_worldLength
	}
	
    agony_internal static function transformOrthoMatrix( x:Number, y:Number, width:Number, height:Number ) : void {
		g_rawData[0]   =   2 / width
		g_rawData[5]   =  -2 / height
		g_rawData[12]  =  -2 * x / width  - 1
		g_rawData[13]  =   2 * y / height + 1
		g_orthoMatrix3d.copyRawDataFrom(g_rawData)
    }
	
	agony_internal static const PRIORITY:int = 8000 // touch
	
	agony_internal static var g_stage:Stage
	agony_internal static var g_stage3d:Stage3D
	agony_internal static var g_context3d:Context3D
	agony_internal static var g_fullWidth:int, g_fullHeight:int, g_bgColor:uint
	agony_internal static var g_enableErrorChecking:Boolean, g_paused:Boolean
	agony_internal static var g_antiAlias:int
	agony_internal static var g_worldList:Array = []
	agony_internal static var g_worldLength:int
	agony_internal static var g_rawData:Vector.<Number> = new <Number>[ 1, 0, 0, 0, 
																		0, 1, 0, 0,
																		0, 0, 1, 0,
																		0, 0, 0, 1]
	agony_internal static var g_orthoMatrix3d:Matrix3D
	
	
	
	private function ____onContextCreated( e:Event ) : void {
		var isContextExisted:Boolean
		
		isContextExisted = Boolean(g_context3d)
		g_context3d = NodeAA.g_context3d = ProgramProxy.g_context3d = g_stage3d.context3D
		
		// enalbe error checking...
		g_context3d.enableErrorChecking = g_enableErrorChecking
		
		// culling...
		g_context3d.setCulling(Context3DTriangleFace.NONE);
		
		// depth test...
		g_context3d.setDepthTest(false, Context3DCompareMode.ALWAYS);
		
		g_context3d.setBlendFactors(Context3DBlendFactor.ONE, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA)
		
		// configure back buffer...
		g_context3d.configureBackBuffer(g_fullWidth, g_fullHeight, g_antiAlias, false)
		
		if(isContextExisted){
			Logger.reportMessage("AgonyAA", "Context has been reset...")
			this.dispatchDirectEvent(AEvent.RESET)
		}
		else{
			Logger.reportMessage("AgonyAA", "Context has been successfully created...")
			this.dispatchDirectEvent(AEvent.BEGINNING)
		}
		
		
		//g_txt = new TextField
		//g_txt.text = "0"
		//g_stage.addChild(g_txt)
	}
	//agony_internal static var g_txt:TextField
	//agony_internal static var g_time:int
	//agony_internal static var g_count:int
	
	
	private function ____onError( e:ErrorEvent ) : void {
		Logger.reportError("AgonyAA", "____onError", "errorID:" + e.errorID + "...text:" + e.text)
	}
	
	final public function update( deltaTime:Number ) : void {
		var i:int
		
		//this.dispatchDirectEvent(AEvent.READY_TO_RENDER)
		
		if (g_paused || !g_context3d) {
			return
		}
		//if(g_context3d.driverInfo == "Disposed"){
			//Logger.reportWarning(this, "update", "Context3d is miss...")
			//return
		//}
		
//		var t:int = getTimer()
		
		//trace("========================================")
		
		// clear...
		g_context3d.clear(((g_bgColor >> 16) & 0xff) / 255.0, 
						  ((g_bgColor >>  8) & 0xff) / 255.0, 
						   (g_bgColor        & 0xff) / 255.0)
		
		// [ pre ]render...
		while (i < g_worldLength) {
			g_worldList[i++].render(false, false)
		}
		
		//g_time += getTimer() - t
		//if(++g_count >= 30){
			//g_txt.text = String(g_time / g_count)
			//g_time = g_count = 0
		//}
		
		// render...
		g_context3d.present()
	}
	
	private static function ____onNewTouch( e:ATouchEvent ) :  void {
		var touch:Touch
		
		touch = e.touch
		____onUpdateInteract(e)
		touch.addEventListener(AEvent.MOVE,    ____onUpdateInteract, PRIORITY)
		touch.addEventListener(AEvent.RELEASE, ____onUpdateInteract, PRIORITY)
	}
	
	private static function ____onUpdateInteract( e:AEvent ) : void {
		var l:int
		var touch:Touch
		
		touch = (e is ATouchEvent) ? (e as ATouchEvent).touch : (e.target as Touch)
		l = g_worldLength
		while (--l > -1) {
			g_worldList[l].handleInteract(touch, e.type)
		}
	}
}
}